package com.demo.fullink.service.impl;

import com.demo.fullink.rabbitmq.MQProducer;
import com.demo.fullink.service.AsyncOrderService;
import com.demo.fullink.service.StorageOrderService;
import com.demo.fullink.service.StorageProductService;
import com.demo.fulllink.common.emum.ResponseErrorEnum;
import com.demo.fulllink.common.utils.CommonUtils;
import com.demo.fulllink.module.po.OrderPO;
import com.demo.fulllink.module.po.ProductPO;
import com.demo.fulllink.module.vo.OrderVO;
import com.demo.fulllink.module.vo.ResponseVO;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class AsyncOrderServiceImpl implements AsyncOrderService {
    
    private static final Logger logger = LoggerFactory.getLogger(AsyncOrderServiceImpl.class);
    
    @Autowired
    private StorageOrderService storageOrderService;
    
    @Autowired
    private StorageProductService storageProductService;
    
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    
    @Autowired
    private MQProducer mqProducer;
    
    @Transactional
    public void processOrder(OrderVO orderVO) {
        if (null != orderVO) {
            //查询库存
            OrderPO dbOrder = storageOrderService.findByID(orderVO.getId());
            if (null != dbOrder) {
                String orderMsg = redisTemplate.opsForValue().get(CommonUtils.getOrderIdempotentKey(dbOrder.getId()));
                //幂等性校验 如果存进行处理
                if (StringUtils.isNotEmpty(orderMsg)) {
                    //校验订单状态
                    if (null != dbOrder && dbOrder.getStatus() == 0) {
                        ResponseVO responseVO = actualPlaceOrder(orderVO);
                        //成功则发送MQ
                        if (responseVO.isSuccess()) {
                            mqProducer.sendNotify(orderVO);
                        }
                    }
                } else {
                    logger.info("拦截到重复请求，丢弃，orderId:{}", orderVO.getId());
                }
            }
        }
    }
    
    /**
     * 实际扣除库存
     *
     * @param orderVO
     * @return
     */
    public ResponseVO actualPlaceOrder(OrderVO orderVO) {
        ResponseVO responseVO = null;
        //CAS 自旋方式 最大努力 扣减库存
        for (int i = 0; i < 3; i++) {
            //获取产品对象
            ProductPO productPO = storageProductService.findByID(orderVO.getProductId());
            //对象为空 退出自旋
            if (null == productPO) {
                responseVO = ResponseErrorEnum.PRODUCT_NOT_EXIST_ERROR.toResponseVO();
                logger.error("商品不存在，productId:{}", orderVO.getProductId());
                break;
            }
            if (productPO.getStock() < orderVO.getProductNum()) {
                responseVO = ResponseErrorEnum.STOCK_ERROR.toResponseVO();
                logger.error("商品库存不足，orderId:{},productId:{}", orderVO.getId(), orderVO.getProductId());
                break;
            }
            //校验数据库库存是否足够
            //设置库存版本号 用于乐观锁校验
            orderVO.setProductVersion(productPO.getVersion());
            //开始调用库存服务进行扣减
            responseVO = storageOrderService.actualPlaceOrder(orderVO);
            //扣款成功直接退出
            if (responseVO.isSuccess()) {
                logger.info("商品库存扣除成功，orderId:{},productId:{}", orderVO.getId(), orderVO.getProductId());
                break;
            } else {
                logger.error("商品库存扣除失败，orderId:{},productId:{}", orderVO.getId(), orderVO.getProductId());
                //失败尝试自旋
                try {
                    //自旋 随机休眠 1-10ms 防止 活锁 提高扣减成功率
                    Thread.sleep((long) (Math.floor(Math.random() * 10) + 1));
                } catch (InterruptedException e) {
                    
                }
                continue;
            }
            
        }
        
        return responseVO;
    }
    
}
